package edu.kit.csl.cratyli.datatypes;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import edu.kit.csl.cratyli.ui.Logger;

/**
 * Instances of this classes or sets of sentences identified by ids. They
 * are usually loaded from a plain text file and a corresponding ids file
 * with {@link #loadFromFiles(String, String)}.
 */
public class TextCorpus implements Iterable<Entry<String, String>> {
	
	Map<String, String> map;
	
	/**
	 * Loads the text corpus from a plain text file and a ids file.
	 * 
	 * @param sentenceFileName path to the plain text file
	 * @param idFileName path to the ids file
	 * @return the loaded text corpus
	 * @throws IOException if one of the files are not readable
	 */
	static public TextCorpus loadFromFiles(String sentenceFileName,
			String idFileName) throws IOException {
		FileInputStream sFstream = new FileInputStream(sentenceFileName);
		DataInputStream sIn = new DataInputStream(sFstream);
		BufferedReader sBr = new BufferedReader(new InputStreamReader(sIn));
		FileInputStream iFstream = new FileInputStream(idFileName);
		DataInputStream iIn = new DataInputStream(iFstream);
		BufferedReader iBr = new BufferedReader(new InputStreamReader(iIn));
		String sLine, iLine;
		TextCorpus corpus = new TextCorpus();
		while ((sLine = sBr.readLine()) != null) {
			iLine = iBr.readLine();
			if (iLine == null) {
				Logger.getSingleton().error("Line number mismatch between "
					+ sentenceFileName + " and " + idFileName);
				break;
			}
			corpus.map.put(iLine.trim(), sLine.trim());
		}
		iIn.close();
		sIn.close();
		return corpus;
	}
	
	/**
	 * Write the corpus to the file system. It creates the files *.ids and
	 * *.plain.trgt.
	 * 
	 * @param outputPrefix prefix for the output files
	 */
	public void writeToFileSystem(String outputPrefix) throws IOException {
		FileWriter fsIds = new FileWriter(outputPrefix + ".ids");
		BufferedWriter outIds = new BufferedWriter(fsIds);
		FileWriter fsPlain = new FileWriter(outputPrefix + ".plain.trgt");
		BufferedWriter outPlain = new BufferedWriter(fsPlain);
		for (Entry<String, String> entry : map.entrySet()) {
			outIds.write(entry.getKey() + "\n");
			outPlain.write(entry.getValue() + "\n");
		}
		outIds.close();
		outPlain.close();
	}
	
	/**
	 * Get the sentence by its ID.
	 * 
	 * @param id the sentence ID
	 * @return the sentence or null if the ID does not exist
	 */
	public String get(String id) {
		return map.get(id);
	}
	
	/**
	 * Remove duplicate words in the corpus.
	 */
	public void removeDuplicates() {
		HashMap<String, String> newSens = new HashMap<String, String>();
		for (Entry<String, String> entry : map.entrySet()) {
			StringBuilder newSen = new StringBuilder();
			String lastWord = null;
			for (String curWord : entry.getValue().split(" ")) {
				if (!curWord.equals(lastWord)) {
					newSen.append(" " + curWord);
				}
				lastWord = curWord;
			}
			newSens.put(entry.getKey(), newSen.toString().trim());
		}
		map = newSens;
	}
	
	/**
	 * Remove words with weights below the given threshold.
	 * 
	 * @param dict Dictionary to look up the weights
	 * @param minWeight the minimum weight of words in the corpus
	 */
	public void filterByWeight(Dictionary dict, double minWeight) {
		HashMap<String, String> newSens = new HashMap<String, String>();
		for (Entry<String, String> entry : map.entrySet()) {
			StringBuilder newSen = new StringBuilder();
			for (String ortho : entry.getValue().split(" ")) {
				Word curWord = dict.get(ortho);
				if (curWord != null && curWord.getWeight() >= minWeight) {
					newSen.append(" " + ortho);
				}
			}
			newSens.put(entry.getKey(), newSen.toString().trim());
		}
		map = newSens;
	}
	
	/**
	 * Put a new sentence into the text corpus. Override existing if the id
	 * already exists.
	 * 
	 * @param id the sentence ID
	 * @param the new sentence (blank separated words)
	 */
	public void put(String id, String sen) {
		map.put(id, sen);
	}
	
	/**
	 * Put all entries in the given map to the corpus. Override existing
	 * IDs in case.
	 * 
	 * @param map Map with ids and their sentences (blank separated words)
	 * @throws NullPointerException if map is null
	 */
	public void putAll(Map<String, String> map) {
		this.map.putAll(map);
	}
	
	/**
	 * Set the whole corpus by a map instance, which assigns each sentence ID
	 * to its written representation.
	 * 
	 * @param corpus The new corpus map to add
	 */
	public void setMap(Map<String, String> map) {
		this.map = map;
	}

	/**
	 * Sole constructor. creates an empty corpus.
	 */
	public TextCorpus() {
		map = new HashMap<String, String>();
	}

	@Override
	public Iterator<Entry<String, String>> iterator() {
		return map.entrySet().iterator();
	}
}
